/// ===========================================================================
/// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
/// KIND, WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
/// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
/// PURPOSE.
/// ===========================================================================
/// 
/// Project:        SharePoint Faceted Search
/// Author:         Leonid Lyublinski (leonidly@microsoft.com)
/// Company:        Microsoft Services
/// Date:           05/04/2007  Version:        1.0
///
/// ===========================================================================
/// $History: $
/// 
/// ===========================================================================

using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.Xml.Serialization;
using System.Data;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.Office.Server;
using Microsoft.Office.Server.Search.Query;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

namespace Microsoft.SharePoint.Portal.ExtendedSearch.WebControls
{
    /// <summary>
    /// Contains utilities to parse/build querystring. Implemented as static methods.
    /// </summary>
    /// <remarks>Implementation of the faceted search relies on </remarks>
    internal class Utility
    {
        /// <summary>
        /// Creates SearchQuery structure from the querystring
        /// </summary>
        /// <returns><seealso cref="SearchQuery"/></returns>
        /// <remarks>Parsing of querystring is based on regular expressions patterns</remarks>
        public static SearchQuery ExtractQuery()
        {
            SearchQuery query = new SearchQuery();
            query.Properties = new NameValueCollection();

            if (HttpContext.Current.Request.QueryString == null) return query;

            // k parameter
            string k = HttpContext.Current.Request.QueryString["k"];
            if (!string.IsNullOrEmpty(k))
            {
                // separate search word from proepreties by space, assuming that keyphrase is 1st
                // look for property by construct <property name>:"<property value>", array of properties should look like
                // xxx: yyy: zzz:.
                // search phrase can be quoted and have a space. Space is a delimiter, yet will look positively fwd
                // skip quoted phrases
                int propertyStart = -1;  //                             -------VVV-------- addition
                string propertyPattern = @"(?<propName>(\w+)):""(?<propValue>([^""]+))""";
                Regex propertyRe = new Regex(propertyPattern, RegexOptions.IgnoreCase);
                try
                {
                    MatchCollection propertyMatches = propertyRe.Matches(k);
                    if (propertyMatches.Count > 0)
                    {
                        propertyStart = propertyMatches[0].Index;

                        foreach (Match property in propertyMatches)
                        {
                            CaptureCollection captures = property.Captures;
                            string propName = property.Groups["propName"].Value;
                            string propValue = property.Groups["propValue"].Value;
                            query.Properties.Add(propName, propValue);
                        }

                        query.Keywords = (propertyStart > 0) ? k.Substring(0, propertyStart-1).Trim() : string.Empty;
                    }
                    else 
                        query.Keywords = k.Trim();
                    
                }
                catch { }
            }

            // s parameter
            query.Scope = HttpContext.Current.Request.QueryString["s"];

            // v parameter
            query.View = HttpContext.Current.Request.QueryString["v"];

            // start parameter
            string start = HttpContext.Current.Request.QueryString["start"];
            query.Start = (string.IsNullOrEmpty(start) ? -1 : int.Parse(start));


            return query;
        }

        /// <summary>
        /// Builds a querystring by joining META properties with keyword, view, scope and start parameters.
        /// </summary>
        /// <param name="query"><see cref="SearchQuery"/></param>
        /// <returns>Returns a generated querystring <see cref="string"/> to use in response redirect</returns>
        public static string BuildQueryString(SearchQuery query)
        {
            string qs = string.Empty;
            ArrayList qsa = new ArrayList();
            if (!string.IsNullOrEmpty(query.Scope))
                qsa.Add("s=" + HttpUtility.UrlEncode(query.Scope));            
            if (!string.IsNullOrEmpty(query.View))
                qsa.Add("v=" + HttpUtility.UrlEncode(query.View));   
            if (query.Start > 0)
                qsa.Add("start=" + HttpUtility.UrlEncode(query.Start.ToString()));

            ArrayList ka = new ArrayList();
            if (!string.IsNullOrEmpty(query.Keywords))
                ka.Add(query.Keywords);
            foreach (string property in query.Properties)
            {
                ka.Add(string.Format("{0}:\"{1}\"",property, query.Properties[property]));
            }
            if (ka.Count > 0)
            {
                string ks = string.Join(" ", (string[]) ka.ToArray(typeof(string)));
                qsa.Add("k=" + HttpUtility.UrlEncode(ks));

            }
            if (qsa.Count>0)
                qs = string.Join("&", (string[]) qsa.ToArray(typeof(string)));

            return qs;
        }
    }

    /// <summary>
    /// The structure holds search parameters, parsed from querystring and intended to build a querystring. 
    /// </summary>
    /// <example>Search string example: chicken Product:"Bisquick" Method:"bake"</example>
    struct SearchQuery
    {
        /// <summary>
        /// Referes to the extract from keyword phrase. "k" in Url
        /// </summary>
        public string Keywords;
        /// <summary>
        /// Refers to the start page. "s" in Url
        /// </summary>
        public int Start;
        /// <summary>
        /// Refers to the view. "v" in Url
        /// </summary>
        public string View;
        /// <summary>
        /// Refers to the scope. "scope" in Url
        /// </summary>
        public string Scope;
        /// <summary>
        /// Referes to the extract from keyword phrase related to the META property. [facet name]:"[facet value]"
        /// </summary>
        public NameValueCollection Properties;
    }
}
